using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Collections;
using System.Timers;

namespace monoServer
{
	public class CommunicatorCommon
	{
		public BugTable sendTelegrams;
		public TcpClient client;
		public IPAddress clientIP;
		public NetworkStream stream;
		public DataManager dm;
		public Queue requestQueue;
		public Queue responseQueue;
		public Queue connectorQueue;
		public Cron cron;
		public Hashtable telegramList;
		public Logger l;
		
		public CommunicatorCommon (TcpClient c, DataManager dm, Logger l)
		{
			this.telegramList = new Hashtable();
			this.telegramList["ping"] = true;
			this.telegramList["login"] = true;
			this.telegramList["logout"] = false;
			this.telegramList["chatmessage"] = false;
			this.telegramList["greeting"] = false;
			
			
			this.l = l;
			this.client = c;
			this.dm = dm;
			this.setUp();
		}
		
		public void setUp()
		{
			this.sendTelegrams = new BugTable("communicator-telegrams", true);
			this.sendTelegrams.addColumn("id", typeof(string));
			this.sendTelegrams.addColumn("timestamp", typeof(DateTime));
			this.sendTelegrams.addColumn("client-ip", typeof(IPAddress));
			this.sendTelegrams.addColumn("session-key", typeof(String));
			this.sendTelegrams.addColumn("telegram", typeof(String));
			this.sendTelegrams.addColumn("ack", typeof(bool)); // Gibt an ob bereits ein acknowledge empfangen wurde
			this.sendTelegrams.addColumn("res", typeof(bool)); // Gibt an ob ein response verlangt wird
			
			this.stream = this.client.GetStream();
			this.clientIP = ((IPEndPoint)this.client.Client.RemoteEndPoint).Address;
			this.stream = this.client.GetStream();
			this.requestQueue = new Queue();
			this.responseQueue = new Queue();
			this.connectorQueue = new Queue();
			this.cron = new Cron();
			this.cron.addJob("checktelegramresponse", new ElapsedEventHandler(this.checkTelegramResponse), 5);
			this.cron.startAllJobs();
			//this.connector = null;
		}
		
		
		public void checkTelegramResponse(object sender, ElapsedEventArgs e)
		{
			//Console.WriteLine("### In checkTelegramResonse, Anzahl Zeilen: " + this.sendTelegrams.countRows());
			for(int i=0; i<=this.sendTelegrams.countRows()-1; i++)
			{
				//Console.WriteLine("Durchlauf: " + i);
				ArrayList al = this.sendTelegrams[i];
				Telegram t = null;
				DateTime dt = (DateTime)al[1];
				//Console.WriteLine("Uhrzeit Telegram: " + dt);
				//Console.WriteLine("Uhrzeit Jetzt:    " + DateTime.Now);
				if(dt.AddSeconds(5).CompareTo(DateTime.Now) < 0)
				{
					t = Telegram.Parse((string)al[4]);
					//Console.WriteLine("T: " + t.toString());
					// Mehr als 5 Sekunden seit dem Senden vergangen
					if(!(bool)al[5])
					{
						// Noch kein Acknowledge, also Telegram nochmal senden
						this.sendTelegrams.deleteRow(i);
						this.resendTelegram(t);
					} else {
						// Acknowledge erhalten
						if(!(bool)al[6])
						{
							// Acknowledge vorhanden, aber kein Response
							// Rausfinden ob ein Response verlangt wird
							if((bool)this.telegramList[t.keyWord])
							{
								// Response wird benötigt
								// TODO Was geschieht wenn kein Response empfangen wird obwohl eines verlangt wird?
								// Evtl. neuer Telegramtyp: Resended Telegram, empfängt eine Seite dieses Telegram muss vorherige Telegram
								// (Das Original) nicht mehr verarbeitet werden, stattdessen das resended Telegram
							} else {
								// Es wird kein Response benötigt. Telegram kann gelöscht werden
								this.sendTelegrams.deleteRow(i);
							}
							
							
						}
					}
				}
				/*
				if(!(bool)al[5])
				{
					// Es wurde noch kein Acknowledge empfangen
					DateTime dt = (DateTime)al[1];
					if(dt.AddSeconds(5).CompareTo(DateTime.Now) > 0)
					{
						// Das Telegram wurde vor mehr als 5 Sekunden gesendet -> Nochmals senden
						this.resendTelegram((string)al[4]);
						this.sendTelegrams.deleteRow(i); // Durch resend wird ein neuer Eintrag in der Tabelle gemacht; Der alte kann gelöscht werden
					}
				} else {
					// Acknowledge empfangen
					if(this.telegramList[
					if(!(bool)al[5])
					{
						// Aber noch kein Response empfangen

					}
				}
				*/				
			}
		}
		
		public void sendLogout()
		{
			//this.sendTelegram(new Telegram("LOGOUT", Telegram.REQUEST, this.dm.currentTelegramKey));
			this.sendTelegram(Telegram.getRequest(this.dm.currentTelegramKey, "LOGOUT"));
		}
		
		public void sendUserData(User u)
		{
			/*
			Telegram t = new Telegram("USERDATA", Telegram.REQUEST, this.dm.currentTelegramKey);
			t.addParameter(u.NickName);
			t.addParameter(u.Password);
			*/
			Telegram t = Telegram.getRequest(this.dm.currentTelegramKey, "USERDATA", u.NickName, u.Password);
			//string tmp = "USERDATA;" + u.ID + ";" + u.NickName + ";" + u.Password;
			this.sendTelegram(t);
		}

		public void sendLoginOk(string key)
		{
			/*
			Telegram t = new Telegram("LOGIN", Telegram.RESPONSE, this.dm.currentTelegramKey);
			t.addParameter(key);
			t.addParameter("OK");
			*/
			Telegram t = Telegram.getResponse(this.dm.currentTelegramKey, key, "OK");
			this.sendTelegram(t);
		}
		
		public void sendLoginNok(string key, string reason)
		{
			/*
			Telegram t = new Telegram("LOGIN", Telegram.RESPONSE, this.dm.currentTelegramKey);
			t.addParameter(key);
			t.addParameter("NOK");
			t.addParameter(reason);
			*/
			
			Telegram t = Telegram.getResponse(this.dm.currentTelegramKey, key, "NOK", reason);
			this.sendTelegram(t);
		}
		
		public Telegram readSingleTelegramAsTelegram()
		{
			return Telegram.Parse(this.readSingleTelegramAsString());
		}
		
		public String readSingleTelegramAsString()
		{
			String commandLine = null;
			String numBytes = null;
			byte[] tmpbyte = new Byte[1];
			byte b = byte.Parse("59");
			tmpbyte = new Byte[1];

			while(!tmpbyte[0].Equals(b))
			{
				// TODO Vorkehrungen treffen dass im Falle eines Falschen Telegrams (falsche oder fehlende byteanzahl keine endlos-schleife entsteht)
				this.stream.Read(tmpbyte, 0, tmpbyte.Length);
				numBytes += Encoding.ASCII.GetString(tmpbyte);
			}
			
			int i = Int16.Parse(numBytes.Substring(0,numBytes.Length-1));
			
			for(int j=0;j<=i-1;j++)
			{
				stream.Read(tmpbyte, 0, tmpbyte.Length);
				commandLine += Encoding.ASCII.GetString(tmpbyte);
			}
			
			//this.l.writeTelegramToFile(numBytes+commandLine, true);
			//Telegram t = Telegram.Parse(numBytes+commandLine);
			//return t;
			this.l.writeTelegramToFile(numBytes+commandLine, true);
			return numBytes+commandLine;
		}
		
		public void sendGreeting(string str)
		{
			//Telegram t = new Telegram("GREETING", Telegram.REQUEST, this.dm.currentTelegramKey);
			Telegram t = Telegram.getRequest(this.dm.currentTelegramKey, "GREETING", str);
			//t.addParameter(str);
			this.sendTelegram(t);
		}
		
		public void sendTelegram(Telegram t)
		{
			string tmp = t.serialize();
			if(!t.type.Equals(Telegram.ACKNOWLEDGE))
			{
				this.sendTelegrams.addRow(t.key, DateTime.Now, this.clientIP, "skey", tmp, false, false);
			}
			this.sendPreparedTelegram(tmp, t.key);
			
		}
		
		public void sendPing(object sender, ElapsedEventArgs e)
		{
			//this.sendTelegram(new Telegram("PING", Telegram.REQUEST, this.dm.currentTelegramKey));
			this.sendTelegram(Telegram.getRequest(this.dm.currentTelegramKey, "PING"));
		}
		
		/*
		public void sendTelegram(Telegram t, bool admin)
		{
			string tmp = t.toString();
			if(!t.keyWord.Equals("ACKNOWLEDGE"))
			{
				//this.sendTelegrams.addRow(t.key, DateTime.Now, this.clientIP, "skey", tmp, false, this.telegramList[t.keyWord.ToLower()]);
				this.sendTelegrams.addRow(t.key, DateTime.Now, this.clientIP, "skey", tmp, false, false, admin);
			}
			this.sendPreparedTelegram(tmp, t.key);
			
		}
		*/
		public void resendTelegram(Telegram t)
		{
			this.sendTelegram(t);
		}
		
		public void sendPreparedTelegram(String t, string key)
		{
			this.l.writeTelegramToFile(t, false);
			byte[] writeBuffer = Encoding.ASCII.GetBytes(t);
			this.stream.Write(writeBuffer, 0, writeBuffer.Length);
			this.stream.Flush();
		}
	}
}